#include <future>
#include <iostream>
#include <chrono>
#include <string>
#include <thread>
#include <queue>

// future 一般用于获得异步访问结果
// 例如线程执行完后返回结果, 由其它线程获得
// 一般适用于执行一次性任务的线程

int do_something(int a, int b)
{
    std::cout << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return a + b;
}

std::string wait_do_something(const std::string& a, const std::string& b)
{
    std::cout << "异步任务执行中..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return a + b;
}

std::packaged_task<int(int, int)> get_task()
{
    std::packaged_task<int(int, int)> task([](int a, int b){
        return a * b;
    });
    return task;
}

std::queue<std::packaged_task<void()>> task_queue;
std::mutex task_mut;
bool stop = false;

void f1() { std::cout << "f1" << std::endl; }
void f2() { std::cout << "f2" << std::endl; }

void thread_task()
{
    while (!stop) {
        std::unique_lock<std::mutex> lk(task_mut);
        std::packaged_task<void()> task;
        if (!task_queue.empty()) {
            task = std::move(task_queue.front());
            task_queue.pop();
        }
        // 在解锁后执行任务比较好 
        if (task.valid()) task();
    }
}

template<class Function>
std::future<void> add_task(Function f)
{
    std::packaged_task<void()> task(f);
    std::future<void> ret = task.get_future();
    std::lock_guard<std::mutex> lk(task_mut);
    task_queue.push(std::move(task));   // 将任务移动到队列中
    return ret; 
}

template<class Function, class ...Args>
std::future<typename std::result_of<Function(Args...)>::type>
spwam_task(Function f, Args... a)
{   
    typedef typename std::result_of<Function(Args...)>::type result_type;
    std::promise<result_type> p;
    std::future<result_type> t = p.get_future();
    p.set_value(f(a...));   // 本来想在另一个线程去执行这个函数, 但是不知道怎么做
    return t;
}


int main(int argc, char* argv[]) {
    // 可以通过 std::async 来启动异步任务
    // 它的第一个参数默认是 std::launch::async | std::launch::deferred
    // std::launch::async 显示要求任务运行在异步线程
    // std::launch::defferred 表示任务会延迟运行, 直到调用 wait 或 get 才开始
    std::future<int> answer = std::async(do_something, 1, 2);
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    std::cout << std::this_thread::get_id() << std::endl;
    std::cout << answer.get() << std::endl;
    std::string a = "hello ";
    std::future<std::string> wait_answer = std::async(std::launch::deferred, wait_do_something, std::ref(a), "world");
    std::cout << "开始等待..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "开始执行异步任务..." << std::endl;
    wait_answer.wait();
    std::cout << "任务结果: " << wait_answer.get() << std::endl;

    // 可以使用 std::packaged_task<> 将一个 future 绑定到一个函数或可调用对象
    // std::packaged_task<> 类模板的参数为函数前面, 类比 std::function
    // std::packaged_task 对象是一个可调用对象, 它可以被封装入一个 std::function 对象, 作为线程函数传给 std::thread
    // 或传给需要调用对象的另一个函数, 或者直接使用, 因此, 可以将任务封装在 std::packaged_task 中, 并在
    // 传到别的地方进行适当调用之前获取 future
    auto task = get_task();
    std::future<int> res = task.get_future();
    std::thread(std::move(task), 4, 5).detach();
    // task(3, 4); 引发 std::future_error
    if (!task.valid()) std::cout << "不拥有合法函数" << std::endl;
    std::cout << res.get() << std::endl;

    std::thread tt(thread_task);

    std::future<void> ref1 = add_task(f1);
    std::future<void> ref2 = add_task(f2);
    std::this_thread::sleep_for(std::chrono::seconds(2));    
    stop = true;
    tt.join();

    // 有时候希望返回值得到的不是函数范围值而是自己设置或者不想绑定已有的函数
    // 而希望重新写一个函数就可以使用 std::promise 的 set_value 方法来设置结果
    // 一个设想的应用场景, 主线程会定时生产一些任务, 每次生产一个任务后, 会将其
    // 交给另外一个线程去处理, 但我们需要在这个任务结束后做一些特殊处理, 这时
    // 我们可以通过在处理线程中 set_value(true) 来表示任务处理成功, 另一个线程
    // 调用 get 获取标识, 然后根据释放处理成功进行自己负责的善后工作, 注意, 对一个
    // std::future 调用两次 get 会引发异常

    bool flag = false;

    auto deal_thread = ([&](std::promise<bool> p){
        std::cout << "开始处理..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
        p.set_value(flag);
        std::cout << "处理完成..." << std::endl;
        flag = !flag;
    });

    auto back_thread = ([&](std::future<bool> f){
        bool res = f.get();
        std::cout << "开始善后..." << std::endl;
        if (res) std::cout << "true" << std::endl;
        else std::cout << "false" << std::endl;
    });

    for (int i = 0; i < 5; i++) {
        // 生产任务
        std::promise<bool> tk;
        std::future<bool> fu = tk.get_future();
        std::thread t1(deal_thread, std::move(tk));
        std::thread t2(back_thread, std::move(fu));
        t1.join();
        t2.join();
    }

    // 注意：如果在异步线程任务中发生了异常, 将会保存在 std::future 中
    // 当调用 get 时会触发这个异常
    std::function<std::string(int, const char*)> func = [](int a, const char* b){
        int res = a + std::atoi(b);
        return std::to_string(res);
    };

    auto f = spwam_task(func, 1, "2");
    std::cout << f.get() << std::endl;


    // std::future 的局限性就是只有一个线程能等待结果, 如果需要多于一个线程等待同一个
    // 事件, 这需要使用 std::shared_future 来代替, std::shared_future 是可复制的
    // 若每个线程通过自身的 shared_future 对象副本访问, 则从多个线程访问同一共享状态是安全的
    std::promise<void> ready_promise;
    // 可以从 std::future 构造 std::shared_future
    std::shared_future<void> ready_future(ready_promise.get_future());  

    auto fun1 = [ready_future](){
        std::cout << "线程1等待中..." << std::endl;
        ready_future.wait();
        std::cout << "线程1收到信号" << std::endl;
    };

    auto fun2 = [ready_future](){
        std::cout << "线程2等待中..." << std::endl;
        ready_future.wait();
        std::cout << "线程2收到信号" << std::endl;
    };

    std::thread r1(fun1);
    std::thread r2(fun2);

    // 保证两个线程都处于等待状态
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "主线程发送信号" << std::endl;
    ready_promise.set_value();

    r1.join();
    r2.join();

    return 0;
}